1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *
19 */
20 package org.apache.mina.util;
21
22 import java.util.Collection;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ConcurrentMap;
27
28 import org.apache.mina.core.buffer.IoBuffer;
29
30 /**
31 * This map is specially useful when reads are much more frequent than writes and
32 * if the cost of instantiating the values is high like allocating an
33 * {@link IoBuffer} for example.
34 *
35 * Based on the final implementation of Memoizer written by Brian Goetz and Tim
36 * Peierls. This implementation will return an
37 * {@link UnsupportedOperationException} on each method that is not intended to
38 * be called by user code for performance reasons.
39 *
40 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
41 * @since MINA 2.0.0-M2
42 */
43 public class LazyInitializedCacheMap<K, V> implements Map<K, V> {
44 private ConcurrentMap<K, LazyInitializer<V>> cache;
45
46 /**
47 * This class provides a noop {@link LazyInitializer} meaning it
48 * will return the same object it received when instantiated.
49 */
50 public class NoopInitializer extends LazyInitializer<V> {
51 private V value;
52
53 public NoopInitializer(V value) {
54 this.value = value;
55 }
56
57 public V init() {
58 return value;
59 }
60 }
61
62 /**
63 * Default constructor. Uses the default parameters to initialize its internal
64 * {@link ConcurrentHashMap}.
65 */
66 public LazyInitializedCacheMap() {
67 this.cache = new ConcurrentHashMap<K, LazyInitializer<V>>();
68 }
69
70 /**
71 * This constructor allows to provide a fine tuned {@link ConcurrentHashMap}
72 * to stick with each special case the user needs.
73 */
74 public LazyInitializedCacheMap(final ConcurrentHashMap<K, LazyInitializer<V>> map) {
75 this.cache = map;
76 }
77
78 /**
79 * {@inheritDoc}
80 */
81 public V get(Object key) {
82 LazyInitializer<V> c = cache.get(key);
83 if (c != null) {
84 return c.get();
85 }
86
87 return null;
88 }
89
90 /**
91 * {@inheritDoc}
92 */
93 public V remove(Object key) {
94 LazyInitializer<V> c = cache.remove(key);
95 if (c != null) {
96 return c.get();
97 }
98
99 return null;
100 }
101
102 /**
103 * If the specified key is not already associated
104 * with a value, associate it with the given value.
105 * This is equivalent to
106 * <pre>
107 * if (!map.containsKey(key))
108 * return map.put(key, value);
109 * else
110 * return map.get(key);</pre>
111 * except that the action is performed atomically.
112 *
113 * @param key key with which the specified value is to be associated
114 * @param value a lazy initialized value object.
115 *
116 * @return the previous value associated with the specified key,
117 * or <tt>null</tt> if there was no mapping for the key
118 * @throws NullPointerException if the specified key or value is null
119 */
120 public V putIfAbsent(K key, LazyInitializer<V> value) {
121 LazyInitializer<V> v = cache.get(key);
122 if (v == null) {
123 v = cache.putIfAbsent(key, value);
124 if (v == null) {
125 return value.get();
126 }
127 }
128
129 return v.get();
130 }
131
132 /**
133 * {@inheritDoc}
134 */
135 public V put(K key, V value) {
136 LazyInitializer<V> c = cache.put(key, new NoopInitializer(value));
137 if (c != null) {
138 return c.get();
139 }
140
141 return null;
142 }
143
144 /**
145 * @throws {@link UnsupportedOperationException} as this method would imply
146 * performance drops.
147 */
148 public boolean containsValue(Object value) {
149 throw new UnsupportedOperationException();
150 }
151
152 /**
153 * @throws {@link UnsupportedOperationException} as this method would imply
154 * performance drops.
155 */
156 public Collection<V> values() {
157 throw new UnsupportedOperationException();
158 }
159
160 /**
161 * @throws {@link UnsupportedOperationException} as this method would imply
162 * performance drops.
163 */
164 public Set<java.util.Map.Entry<K, V>> entrySet() {
165 throw new UnsupportedOperationException();
166 }
167
168 /**
169 * {@inheritDoc}
170 */
171 public void putAll(Map<? extends K, ? extends V> m) {
172 for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
173 cache.put(e.getKey(), new NoopInitializer(e.getValue()));
174 }
175 }
176
177 /**
178 * {@inheritDoc}
179 */
180 public Collection<LazyInitializer<V>> getValues() {
181 return cache.values();
182 }
183
184 /**
185 * {@inheritDoc}
186 */
187 public void clear() {
188 cache.clear();
189 }
190
191 /**
192 * {@inheritDoc}
193 */
194 public boolean containsKey(Object key) {
195 return cache.containsKey(key);
196 }
197
198 /**
199 * {@inheritDoc}
200 */
201 public boolean isEmpty() {
202 return cache.isEmpty();
203 }
204
205 /**
206 * {@inheritDoc}
207 */
208 public Set<K> keySet() {
209 return cache.keySet();
210 }
211
212 /**
213 * {@inheritDoc}
214 */
215 public int size() {
216 return cache.size();
217 }
218 }